use std::fs;
mod utils;
use std::env;
use utils::parse_size;

const FLASH_BASE: usize = 0x08000000;
const RAM_BASE: usize = 0x20000000;

#[cfg(feature = "rtos")]
const RTOS_INTERRUPT: &'static str = r#"
PROVIDE(SysTick = SysTick_Handler);
PROVIDE(PendSV = PendSV_Handler);
PROVIDE(SVCall = SVC_Handler);
"#;

#[cfg(not(feature = "ota"))]
fn no_ota() -> String {
    let flash = env::var("FLASH").expect("no flash");
    let ram = env::var("RAM").expect("no ram");
    
    let flash = parse_size(&flash);
    let ram = parse_size(&ram);

    format!(
        r#"
MEMORY
{{
    FLASH : ORIGIN = 0x{:08x}, LENGTH = 0x{:0x}
    RAM : ORIGIN = 0x{:08x}, LENGTH = 0x{:0x}
}}
"#,
        FLASH_BASE, flash, RAM_BASE, ram
    )
}

#[cfg(all(feature = "ota", feature = "bootloader"))]
fn use_bootloader() -> String {
    let ram = env::var("RAM").expect("no ram");
    let bootloader = env::var("BOOTLOADER").expect("no bootloader");
    let boot_info = env::var("BOOT_INFO").expect("no boot info");

    let ram = parse_size(&ram);
    let bootloader = parse_size(&bootloader);
    let boot_info = parse_size(&boot_info);

    format!(
        r#"
MEMORY
{{
    FLASH : ORIGIN = 0x{:08x}, LENGTH = 0x{:0x}
    RAM : ORIGIN = 0x{:08x}, LENGTH = 0x{:0x}
}}
"#,
        FLASH_BASE, 
        bootloader, 
        RAM_BASE + boot_info, 
        ram - boot_info
    )
}

#[cfg(all(feature = "ota", not(feature = "bootloader")))]
fn use_app() -> String {
    let app = env::var("APP").expect("no app");
    let ram = env::var("RAM").expect("no ram");
    let bootloader = env::var("BOOTLOADER").expect("no bootloader");
    let boot_info = env::var("BOOT_INFO").expect("no boot info");
    
    let app = parse_size(&app);
    let ram = parse_size(&ram);
    let bootloader = parse_size(&bootloader);
    let boot_info = parse_size(&boot_info);

    format!(
        r#"
MEMORY
{{
    FLASH : ORIGIN = 0x{:08x}, LENGTH = 0x{:0x}
    RAM : ORIGIN = 0x{:08x}, LENGTH = 0x{:0x}
}}
"#,
        FLASH_BASE + bootloader, 
        app, 
        RAM_BASE + boot_info,
        ram - boot_info
    )
}

#[cfg(feature = "ota")]
fn use_ota() -> String {
  
    #[cfg(feature = "bootloader")]
    {
        return use_bootloader();
    }

    #[cfg(not(feature = "bootloader"))]
    {
        return use_app();
    }
}

fn basic() -> String {
    #[cfg(feature = "ota")]
    {
        return use_ota();
    }

    #[cfg(not(feature = "ota"))]
    {
        return no_ota();
    }
}

pub fn create_memoryx() {
    let mut content = String::new();
    content.push_str(&basic());

    #[cfg(feature = "rtos")]
    {
        content.push_str(RTOS_INTERRUPT);
    }

    fs::write("./memory.x", content).unwrap();
}
